/*
 * handleDownload function
 * Info:
 * This function is used to handle incoming download requests.
 * e.g. a user wants to download a file from the server.
 */

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mman.h> //memory map files dingen
#include <semaphore.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h> //to determine filesize with fstat()
#include <string.h>

#define DOWNMAPFILE "/tmp/mmappeddownload.bin" //map file for downloads

int sendData(long chunkSize, long unsigned fileSize, int clientFd, int downFd, int comChannel, int fileFd);

int handleDownload(char *pathname, char *filename, int comChannel)
{
    int downfd = 0;
    int filefd = 0;
    unsigned long fileSize = 0;
    long chunkSize;
    sem_t *semComChanRead;
    sem_t *semComChanPost;
    struct stat buf; //struct for information on a file opened with a descriptor

    //Opening semaphores
    if((semComChanPost = sem_open("semComChanPost", 0))== SEM_FAILED)
    {
        perror("semopen semComChanPost: ");
    }
    if((semComChanRead = sem_open("semComChanRead", 0))== SEM_FAILED)
    {
        perror("semopen semComChanRead: ");
    }
    //end of semaphore opening

    //open our download memory map file
    downfd = open(DOWNMAPFILE, O_RDWR);
    if(downfd < 0)
    {
        perror("Could not open the download map file!");
        exit(EXIT_FAILURE);
    }
    //end of download memory map file opening

    //open the requested file, if it does not exist, give an error
    if((filefd = open(filename, O_RDONLY))<0)
    {
        perror("can't open file");
        write(comChannel, "ERROROPENFILE\0", 14);
        close(filefd);
    }
    //end of file opening

    //calculate the filesize (this must be known so we know when we're done)
    fstat(filefd, &buf); //get information on the file in filefd
    fileSize = buf.st_size; //fileSize gets the amount of bytes known in st_size
    printf("fileSize: %lu\n", fileSize);
    //return 0;
    //end of calculation

    //determing what chunkSize we have to use
    if (fileSize < 1024) { chunkSize = fileSize;} //chunkRemain = 0; }                                     //< 1KB
    else if (fileSize < 524323) { chunkSize = fileSize / 2;} //chunkRemain = fileSize % 2; }               //< 512KB
    else if (fileSize < 1048576) { chunkSize = fileSize / 4;} //chunkRemain = fileSize % 4; }              //< 1MB
    else if (fileSize < 5242880) { chunkSize = fileSize / 8;} //chunkRemain = fileSize % 8; }              //< 5MB
    else if (fileSize < 10485760) { chunkSize = fileSize / 16;} //chunkRemain = fileSize % 16; }           //< 10MB
    else if (fileSize < 15728640) { chunkSize = fileSize / 28;} //chunkRemain = fileSize % 28; }           //< 15MB
    else if (fileSize < 20971520) { chunkSize = fileSize / 44;} //chunkRemain = fileSize % 44; }           //< 20MB
    else if (fileSize < 31457280) { chunkSize = fileSize / 60;} //chunkRemain = fileSize % 60; }           //< 30MB
    else if (fileSize < 41943040) { chunkSize = fileSize / 70;} //chunkRemain = fileSize % 70; }           //< 40MB
    else if (fileSize < 62914560) { chunkSize = fileSize / 85;} //chunkRemain = fileSize % 85; }           //< 60MB
    else if (fileSize < 104857600) { chunkSize = fileSize / 100;} //chunkRemain = fileSize % 100; }        //< 100MB
    else if (fileSize < 1572864000) { chunkSize = fileSize / 130;} //chunkRemain = fileSize % 130; }       //< 150MB
    else if (fileSize < 3145728000) { chunkSize = fileSize / 200;} //chunkRemain = fileSize % 200; }       //< 300MB
    else if (fileSize > 3145728000) { chunkSize = fileSize / 220;} //chunkRemain = fileSize % 220; }       //> 300MB
    //end of determination
    //printf("chunkRemain: %lu\n", chunkRemain);
    sendData(chunkSize, fileSize, 0, downfd, comChannel, filefd);
    return 0;
}
//end of handleDownload

int sendData(long chunkSize, unsigned long fileSize, int clientNo, int downFd, int comChannel, int fileFd)
{
    sem_t *semDownEmpty;
    sem_t *semDownFull;
    char *downMap;
    char packet[sizeof(chunkSize)+2];
    unsigned long amountSent = 0;

    //opening semaphores
    if((semDownEmpty = sem_open("semDownEmpty", 0))== SEM_FAILED)
    {
        perror("semopen semDownEmpty: ");
    }
    if((semDownFull = sem_open("semDownFull", 0))== SEM_FAILED)
    {
        perror("semopen semDownFull: ");
    }
    //end of opening semaphores

    if((downMap = malloc(sizeof(packet)))<0)
    {
        perror("could not malloc downMap\n");
        return 0;
    }

    while(amountSent != fileSize)
    {
        sem_wait(semDownEmpty);
        bzero(packet, sizeof(chunkSize)+3);

        read(fileFd, &packet, amountSent+chunkSize);
        amountSent += chunkSize;
        packet[amountSent+1] = clientNo;
        packet[amountSent+2] = '\0';

        if(write(downFd, packet, sizeof(packet))<0)
        {
            perror("error writing packet to downFd\n");
        }
        sem_post(semDownFull);
    }
    return 0;
}